﻿using Microsoft.Extensions.Logging;
using MyWeb.Extensions.RabbitMQ.Model;
using RabbitMQ.Client;
using RabbitMQ.Client.Events;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace MyWeb.Extensions.RabbitMQ.MqConnection
{
    internal class MqConnectionHelper
    {
        #region RabbitMQ参数定义
        /*
         备注：使用数组的部分，是给消费端用的。目前生产者只设置了一个，消费者可能存在多个。
                     当然，有条件的还可以上RabbitMQ集群进行处理，会更好玩一点。
         */

        /// <summary>
        /// RabbitMQ工厂：发送端
        /// </summary>
        private static IConnectionFactory? _connectionSendFactory;
        /// <summary>
        /// RabbitMQ工厂：接收端
        /// </summary>
        private static IConnectionFactory[]? _connectionReceiveFactory;
        /// <summary>
        /// 连接：发送端
        /// </summary>
        private static IConnection? _connectionSend;
        /// <summary>
        /// 连接：消费端
        /// </summary>
        private static IConnection[]? _connectionReceive;
        /// <summary>
        /// 配置信息
        /// </summary>
        public static List<MqConfigInfo>? _mqConfig;
        /// <summary>
        /// 通道：发送端
        /// </summary>
        private static IModel? _modelSend;
        /// <summary>
        /// 通道：消费端
        /// </summary>
        private static IModel[]? _modelReceive;

        /// <summary>
        /// 事件
        /// </summary>
        private static EventingBasicConsumer[]? _basicConsumer;
        #endregion

        private readonly ILogger<MqConnectionHelper> _logger;
        public MqConnectionHelper(ILogger<MqConnectionHelper> logger)
        {
            _logger = logger;

            _connectionReceiveFactory = new IConnectionFactory[_costomerCount];
            _connectionReceive = new IConnection[_costomerCount];
            _modelReceive = new IModel[_costomerCount];
            _basicConsumer = new EventingBasicConsumer[_costomerCount];

        }

        /// <summary>
        /// 消费者数量
        /// </summary>
        public static int _costomerCount = 2;
        public static string[] _routingKey = new string[] { "WeskyNet001", "WeskyNet002" };
        public static string[] _queueName = new string[] { "Queue001", "Queue002" };

        /// <summary>
        /// 生产者初始化连接配置
        /// </summary>
        public void SendFactoryConnectionInit()
        {
            #pragma warning disable CS8604 // 引用类型参数可能为 null。
            _connectionSendFactory = new ConnectionFactory
            {
                HostName = _mqConfig?.FirstOrDefault().Host,
                Port = _mqConfig.FirstOrDefault().Port,
                UserName = _mqConfig?.FirstOrDefault().User,
                Password = _mqConfig?.FirstOrDefault().Password
            };
            #pragma warning restore CS8604 // 引用类型参数可能为 null。
        }

        /// <summary>
        /// 生产者连接
        /// </summary>
        public void SendFactoryConnection()
        {
            if (null != _connectionSend && _connectionSend.IsOpen)
            {
                // 已有连接
                return;
            }
            // 创建生产者连接
            _connectionSend = _connectionSendFactory.CreateConnection();

            if (null != _modelSend && _modelSend.IsOpen)
            {
                // 已有通道
                return;
            }
            // 创建生产者通道
            _modelSend = _connectionSend.CreateModel();
            // 定义交换机名称和类型(direct)
            _modelSend.ExchangeDeclare(_mqConfig?.FirstOrDefault().ExchangeName, ExchangeType.Direct);

        }

        /// <summary>
        /// 消费者初始化连接配置
        /// </summary>
        public void ReceiveFactoryConnectionInit()
        {
            #pragma warning disable CS8604 // 引用类型参数可能为 null。
            var factories = new ConnectionFactory
            {
                HostName = _mqConfig.FirstOrDefault().Host,
                Port = _mqConfig.FirstOrDefault().Port,
                UserName = _mqConfig.FirstOrDefault().User,
                Password = _mqConfig.FirstOrDefault().Password
            };
            #pragma warning restore CS8604 // 引用类型参数可能为 null。

            for (int i = 0; i < _costomerCount; i++)
            {
                _connectionReceiveFactory[i] = factories;  // 给每个消费者绑定一个连接工厂
            }
        }

        /// <summary>
        /// 消费者连接
        /// </summary>
        /// <param name="consumeIndex"></param>
        /// <param name="exchangeName">交换机名称</param>
        /// <param name="routeKey">消费者</param>
        /// <param name="queueName">队列名称</param>
        public void ConnectionReceive(int consumeIndex, string exchangeName, string routeKey, string queueName)
        {
            _logger.LogInformation($"开始连接RabbitMQ消费者：{routeKey}");

            if (null != _connectionReceive[consumeIndex] && _connectionReceive[consumeIndex].IsOpen)
            {
                //已有消费者连接
                return;
            }
            // 创建消费者连接
            _connectionReceive[consumeIndex] = _connectionReceiveFactory[consumeIndex].CreateConnection();

            if (null != _modelReceive[consumeIndex] && _modelReceive[consumeIndex].IsOpen)
            {
                //已有消费者通道
                return;
            }
            // 创建消费者通道
            _modelReceive[consumeIndex] = _connectionReceive[consumeIndex].CreateModel();

            _basicConsumer[consumeIndex] = new EventingBasicConsumer(_modelReceive[consumeIndex]);
            // 定义交换机名称和类型  与生产者保持一致
            _modelReceive[consumeIndex].ExchangeDeclare(exchangeName, ExchangeType.Direct);
            // 定义消费者队列
#pragma warning disable CS8604 // 引用类型参数可能为 null。
            _modelReceive[consumeIndex].QueueDeclare(
                         queue: queueName, //消息队列名称
                         durable: _mqConfig.FirstOrDefault().Durable, // 是否可持久化，此处配置在文件中，默认全局持久化(true)，也可以自定义更改
                         exclusive: false,
                         autoDelete: false,
                         arguments: null
           );
#pragma warning restore CS8604 // 引用类型参数可能为 null。

            // 队列绑定给指定的交换机
            _modelReceive[consumeIndex].QueueBind(queueName, exchangeName, routeKey);
            // 设置消费者每次只接收一条消息
            _modelReceive[consumeIndex].BasicQos(0, 1, false);

            StartListener((model, ea) =>
            {
                // 接收到的消息
                byte[] message = ea.Body.ToArray();

                string msg = Encoding.UTF8.GetString(message);

                _logger.LogInformation($"队列{queueName}接收到消息:{msg}");
                Thread.Sleep(2000);

                _modelReceive[consumeIndex].BasicAck(ea.DeliveryTag, true);
            }, queueName, consumeIndex);

        }

        /// <summary>
        /// 消费者接收消息的确认机制
        /// </summary>
        /// <param name="basicDeliverEventArgs"></param>
        /// <param name="queueName">队列名</param>
        /// <param name="consumeIndex"></param>
        private static void StartListener(EventHandler<BasicDeliverEventArgs> basicDeliverEventArgs, string queueName, int consumeIndex)
        {
            _basicConsumer[consumeIndex].Received += basicDeliverEventArgs;
            _modelReceive[consumeIndex].BasicConsume(queue: queueName, autoAck: false, consumer: _basicConsumer[consumeIndex]); // 设置手动确认。
        }

        /// <summary>
        /// 消息发布
        /// </summary>
        /// <param name="message">消息</param>
        /// <param name="exchangeName">交换机名</param>
        /// <param name="routingKey"></param>
        public static void PublishExchange(string message, string exchangeName, string routingKey = "")
        {
            byte[] body = Encoding.UTF8.GetBytes(message);
            _modelSend.BasicPublish(exchangeName, routingKey, null, body);
        }
    }
}
